001 package EVolve.util.settings; 002 003 import java.io.*; 004 import java.util.*; 005 006 public class IniFile { 007 private Map sections; 008 private File file; 009 private boolean modified; 010 011 public IniFile(String fileName) throws IOException { 012 this(new File(fileName)); 013 } 014 015 public IniFile(File file) throws IOException { 016 this.sections = new LinkedHashMap(); 017 this.file = file; 018 019 if (file.exists()) { 020 load(); 021 } 022 } 023 024 private static String parseValue(String value) { 025 if (value == null) { 026 return null; 027 } 028 029 value = value.trim(); 030 if (value.length() > 0) { 031 char s[] = value.toCharArray(); 032 if (s[0] == '"') { 033 // Find the next '"' 034 boolean escaped = false; 035 int i; 036 loop: 037 for (i = 1; i < s.length; i++) { 038 if (escaped) { 039 escaped = false; 040 } else { 041 switch (s[i]) { 042 case '\\': 043 escaped = true; 044 break; 045 case '"': 046 break loop; 047 default: 048 break; 049 } 050 } 051 } 052 053 if (s[i] == '"') { 054 if (i < s.length - 1) { 055 String commentStr = value.substring(i+1).trim(); 056 if (!commentStr.startsWith(";")) { 057 throw new RuntimeException("Expression expected to be a comment"); 058 } 059 } 060 value = value.substring(1, i); 061 } 062 } 063 } 064 065 return unescape(value); 066 } 067 068 public static String escape(String value) { 069 if (value == null) { 070 return null; 071 } 072 073 String result = ""; 074 char[] s = value.toCharArray(); 075 for (int i = 0; i < s.length; i++) { 076 char c = s[i]; 077 switch (c) { 078 case '?': 079 result += "\\?"; 080 break; 081 case '\b': 082 result += "\\b"; 083 break; 084 case '\t': 085 result += "\\t"; 086 break; 087 case '\n': 088 result += "\\n"; 089 break; 090 case '\f': 091 result += "\\f"; 092 break; 093 case '\r': 094 result += "\\r"; 095 break; 096 case '"': 097 result += "\\\""; 098 break; 099 case '\'': 100 result += "\\'"; 101 break; 102 case '\\': 103 result += "\\\\"; 104 break; 105 default: 106 result += c; 107 break; 108 } 109 } 110 111 return result; 112 } 113 114 public static String unescape(String value) { 115 if (value == null) { 116 return null; 117 } 118 119 String result = ""; 120 char[] s = value.toCharArray(); 121 for (int i = 0; i < s.length; i++) { 122 char c = s[i]; 123 if (c == '\\') { 124 if (i == (s.length - 1)) { 125 return result + '\\'; 126 } else { 127 c = s[++i]; 128 switch (c) { 129 case '?': 130 result += '?'; 131 break; 132 case 'b': 133 result += '\b'; 134 break; 135 case 't': 136 result += '\t'; 137 break; 138 case 'n': 139 result += '\n'; 140 break; 141 case 'f': 142 result += '\f'; 143 break; 144 case 'r': 145 result += '\r'; 146 case 'x': 147 { 148 i++; 149 String hexString = ""; 150 while (i < s.length) { 151 c = s[i]; 152 if (Character.digit(c, 16) >= 0) { 153 hexString += c; 154 i++; 155 } else { 156 break; 157 } 158 } 159 160 if (hexString.length() <= 0) { 161 i--; 162 result += "\\x"; 163 } else { 164 result += (char) Short.parseShort(hexString, 16); 165 } 166 } 167 break; 168 case '0': 169 case '1': 170 case '2': 171 case '3': 172 case '4': 173 case '5': 174 case '6': 175 case '7': 176 case '8': 177 case '9': 178 { 179 String octalString = ""; 180 while (i < s.length) { 181 c = s[i]; 182 if (Character.digit(c, 8) >= 0) { 183 octalString += c; 184 i++; 185 } else { 186 break; 187 } 188 } 189 190 if (octalString.length() <= 0) { 191 result += "\\"; 192 } else { 193 result += (char) Short.parseShort(octalString, 8); 194 } 195 } 196 break; 197 default: 198 result += '\\' + c; 199 break; 200 } 201 } 202 } else { 203 result += c; 204 } 205 } 206 207 return result; 208 } 209 210 public void load() throws IOException { 211 BufferedReader in = new BufferedReader(new FileReader(file)); 212 String line; 213 String currentSection = null; 214 215 while ((line = in.readLine()) != null) { 216 line = line.trim(); 217 if (line.length() <= 0) { 218 continue; 219 } 220 221 char c = line.charAt(0); 222 223 switch (c) { 224 case ';': 225 continue; 226 case '[': 227 currentSection = line.substring(1, line.length() - 1); 228 put(currentSection); 229 break; 230 default: 231 { 232 int eqPos = line.indexOf('='); 233 if (eqPos < 0) { 234 throw new RuntimeException("Invalid INI file format -- Expected key=value"); 235 } 236 237 String key = line.substring(0, eqPos).trim(); 238 String value = parseValue(line.substring(eqPos + 1)); 239 240 put(currentSection, key, value); 241 } 242 break; 243 } 244 } 245 246 modified = false; 247 248 in.close(); 249 } 250 251 public void write() throws IOException { 252 253 PrintStream out = new PrintStream(new FileOutputStream(file)); 254 255 Iterator sectionIt = sections.keySet().iterator(); 256 while (sectionIt.hasNext()) { 257 Object sectionName = (String) sectionIt.next(); 258 Map keysToValues = (Map) sections.get(sectionName); 259 260 out.println("[" + sectionName + "]"); 261 Iterator keysIt = keysToValues.keySet().iterator(); 262 while (keysIt.hasNext()) { 263 String key = (String) keysIt.next(); 264 String value = (String) keysToValues.get(key); 265 out.println(key + "=\"" + escape(value) + "\""); 266 } 267 out.println(); 268 } 269 } 270 271 public boolean contains(String section) { 272 if (section == null) { 273 return false; 274 } 275 276 return sections.containsKey(section); 277 } 278 279 public boolean contains(String section, String key) { 280 if (section == null || key == null) { 281 return false; 282 } 283 284 if (sections.containsKey(section)) { 285 Map keysToValues = (Map) sections.get(section); 286 return keysToValues.containsKey(key); 287 } 288 289 return false; 290 } 291 292 public boolean put(String section) { 293 if (section == null) { 294 return false; 295 } 296 297 if (!sections.containsKey(section)) { 298 sections.put(section, new LinkedHashMap()); 299 } 300 301 modified = true; 302 return true; 303 } 304 305 public boolean put(String section, String key, String value) { 306 if (section == null || key == null) { 307 return false; 308 } 309 310 if (value == null) { 311 value = ""; 312 } 313 314 Map keysToValues; 315 if (sections.containsKey(section)) { 316 keysToValues = (Map) sections.get(section); 317 } else { 318 keysToValues = new LinkedHashMap(); 319 sections.put(section, keysToValues); 320 } 321 322 keysToValues.put(key, value); 323 324 modified = true; 325 326 return true; 327 } 328 329 public String get(String section, String key) { 330 if (sections.containsKey(section)) { 331 Map keysToValues = (Map) sections.get(section); 332 if (keysToValues.containsKey(key)) { 333 return (String) keysToValues.get(key); 334 } 335 } 336 337 return null; 338 } 339 340 public Set getSections() { 341 return new HashSet(sections.keySet()); 342 } 343 344 public Set getKeys(String section) { 345 if (section == null) { 346 return null; 347 } 348 349 if (sections.containsKey(section)) { 350 Map keysToValues = (Map) sections.get(section); 351 return new HashSet(keysToValues.keySet()); 352 } 353 354 return null; 355 } 356 357 public Set getValues(String section) { 358 if (section == null) { 359 return null; 360 } 361 362 if (sections.containsKey(section)) { 363 Map keysToValues = (Map) sections.get(section); 364 return new HashSet(keysToValues.values()); 365 } 366 367 return null; 368 } 369 370 public void close() throws IOException { 371 if (sections != null) { 372 if (modified) { 373 this.write(); 374 } 375 sections = null; 376 } 377 } 378 379 public void finalize() { 380 try { 381 this.close(); 382 } catch (IOException e) { 383 // not much we can do now... 384 } 385 } 386 } 387